#include <stdint.h>
#include "dw_uart.h"
#include "reg_common.h"

#define UART_DLF_EN             1      // enable frac config operation

//UART Registers
#define UART_CLK                200000000
#define UART_CONSOLE_BAUDRATE   115200
// #define uart_reg_base           0xff027000
#define uart_reg_base           0xD0018000
#define UART_RBR                0x0000 //Receive Buffer Register
#define UART_THR                0x0000 //Transmit Holding Register
#define UART_DLL                0x0000 //Divisor Latch Low Byte Register,  can write it when UART_LCR[7]=1
#define UART_DLH                0x0004 //Divisor Latch High Byte Register, can write it when UART_LCR[7]=1
#define UART_IER                0x0004 //Interrupt Enable Register 
#define UART_IIR                0x0008 //Interrupt Identification Register, Read-Only
#define UART_FCR                0x0008 //FIFO Control Register, Write-Only
#define UART_LCR                0x000C //Line Control Register
#define UART_MCR                0x0010 //Modem Control Register
#define UART_LSR                0x0014 //Line Status Register
#define UART_MSR                0x0018 //Modem Status Register
#define UART_DLF                0x00c0 //frac Register
//#define UART_SCR                0x001C //Scractch Register 暂存寄存器
//#define UART_ISR                0x0020 //Infrared Selection Register
//#define UART_FOR                0x0024 //Receive FIFO Occupancy Register
//#define UART_ABR                0x0028 //Auto-Baud Control Register
//#define UART_ACR                0x002C //Auto-Baud Count Register

static int fls(int x)
{
        int r = 32;

        if (!x)
                return 0;
        if (!(x & 0xffff0000u)) {
                x <<= 16;
                r -= 16;
        }
        if (!(x & 0xff000000u)) {
                x <<= 8;
                r -= 8;
        }
        if (!(x & 0xf0000000u)) {
                x <<= 4;
                r -= 4;
        }
        if (!(x & 0xc0000000u)) {
                x <<= 2;
                r -= 2;
        }
        if (!(x & 0x80000000u)) {
                x <<= 1;
                r -= 1;
        }
        return r;
}

#define DIV_ROUND_CLOSEST(x, divisor)(			\
{							\
	typeof(x) __x = x;				\
	typeof(divisor) __d = divisor;			\
	(((typeof(x))-1) > 0 ||				\
	 ((typeof(divisor))-1) > 0 ||			\
	 (((__x) > 0) == ((__d) > 0))) ?		\
		(((__x) + ((__d) / 2)) / (__d)) :	\
		(((__x) - ((__d) / 2)) / (__d));	\
}							\
)

static unsigned int dw8250_get_divisor(unsigned int uartclk, unsigned int dlf_size, unsigned int baud, unsigned int *frac)
{
	unsigned int quot, rem, base_baud = baud * 16;

	quot = uartclk / base_baud;
	rem = uartclk % base_baud;
	*frac = DIV_ROUND_CLOSEST(rem << dlf_size, base_baud);

	return quot;
}

#if UART_DLF_EN// scalable but big code
static unsigned int dw8250_get_dlfsize()
{
	uint32_t reg = uart_reg_base;
	unsigned int rdata;

	write32(reg+UART_DLF, ~0U);
	rdata = read32(reg+UART_DLF);
	write32(reg+UART_DLF, 0);

	return fls(rdata);
}
#else
#define dw8250_get_dlfsize()	(4)
#endif

void api_uart_putc(char ch)
{
    while (((read32(uart_reg_base+UART_LSR)) & (0x40)) == 0);
    // if((read32(uart_reg_base+UART_LSR)) & (0x60))
    // {
	write32(uart_reg_base + UART_THR, (int)ch);
    // }
}

int api_uart_getc(void)
{
    int ret = -1;
//    int result = (read32(uart_reg_base+UART_LSR)) & (0x01);
    while(!((read32(uart_reg_base+UART_LSR)) & (0x01)));
    
    ret = read32(uart_reg_base);
    
    return ret;
}

void uart_init(void)
{
    uint32_t divisor, frac;
	uint32_t reg = uart_reg_base;
    if(UART_DLF_EN) {
        divisor = dw8250_get_divisor(UART_CLK, dw8250_get_dlfsize(), UART_CONSOLE_BAUDRATE, &frac);
        write32(reg+UART_DLF, frac);
    }
    else {
        divisor = UART_CLK/(16 * UART_CONSOLE_BAUDRATE);
    }
    // set lcr to 0x03, 8 data bits per character 
    write32(uart_reg_base+UART_LCR, 0x03);
    // 使能 uart,rx_intr
    write32(uart_reg_base+UART_IER, 0x00);    //0: close int
    write32(uart_reg_base+UART_IER, 0x41);    //0: close int
    write32(uart_reg_base+UART_FCR, 0x00);    //0:no FIFO

    //set 波特率 //Divisor = {UART_DLH[7:0], UART_DLL[7:0]}
    write32(uart_reg_base+UART_LCR, 0x80);   //[7]=1: Access UART_DLH & UART_DLL

    // write32(0xD1012018, divisor);
    write32(uart_reg_base+UART_DLH, (divisor >> 8) & 0xff); 
    write32(uart_reg_base+UART_DLL, divisor & 0xff);

    //无奇偶检验，2个停止位，数据位一包数据8bit
    // write32(uart_reg_base+UART_LCR, read32(uart_reg_base+UART_LCR) & ~0x80);
    write32(uart_reg_base+UART_LCR, 0x0);    //[7]=0: Access UART_RBR & UART_THR & UART_IER
    for(int i=0; i<3; i++){}
    write32(uart_reg_base+UART_LCR, 0x3);    //[3]=0: No Parity Enable; [2]=1: 2 Stop Bits; [1:0]=0x3: 8 bit-charactor
    
}
